package org.extremengine.common;

/**
 * 速度矢量。
 * 
 * @author yuxing
 * 
 */
public class Velocity {

	public static final int PI_DEGRESS = 180;
	
	public static final int ANGLE_DEGREE_FORWARD = 0;
	public static final int ANGLE_DEGREE_UPWARD = 90;
	public static final int ANGLE_DEGREE_BACKWARD = 180;
	public static final int ANGLE_DEGREE_DOWNWARD = 270;

	// Unit is m/s or mm/ms
	private double speed;

	// if > 0, angle = 0; if < 0, angle = 180
	private double speedx;

	// if > 0, angle = 90; if < 0, angle = 270
	private double speedy;

	// 0-360
	private double angleDegree;
	
	// 
	private double accelx;
	
	// 
	private double accely;

	/**
	 * 值为0的速度矢量
	 * 
	 * @param angle
	 */
	public Velocity(double angle) {
		super();
		this.angleDegree = angle;
	}

	public Velocity(double value, double angle) {
		super();
		this.speed = value;
		this.angleDegree = angle;
		this.calculateVectorProjection();
	}
	
//	public Velocity(double speedx, double speedy, double angle) {
//		super();
//		this.speedx = speedx;
//		this.speedy = speedy;
//		this.angleDegree = angle;
//		this.calculateVectorProjection();
//	}
	
	public double getSpeed() {
		return speed;
	}

	public void setSpeed(double value) {
		this.speed = value;
		this.calculateVectorProjection();
	}

	public double getSpeedx() {
		return speedx;
	}

	public void setSpeedx(double speedx) {
		this.speedx = speedx;
		this.calculateJoinVector();
	}
	
	public void increaseSpeedx(double deltax) {
		this.setSpeedx(this.getSpeedx() + deltax);
	}

	public double getSpeedy() {
		return speedy;
	}

	public void setSpeedy(double speedy) {
		this.speedy = speedy;
		this.calculateJoinVector();
	}
	
	public void increaseSpeedy(double deltay) {
		this.setSpeedy(this.getSpeedy() + deltay);
	}

	public void setSpeedxy(double speedx, double speedy) {
		this.speedx = speedx;
		this.speedy = speedy;
		this.calculateJoinVector();
	}
	
	public void increaseSpeedxy(double deltax, double deltay) {
		this.setSpeedxy(this.getSpeedx() + deltax, this.getSpeedy() + deltay);
	}

	public double getAngleDegree() {
		return angleDegree;
	}

	public void setAngleDegree(double angleDegree) {
		this.angleDegree = angleDegree;
		this.calculateVectorProjection();
	}
	
	public double getRadian() {
		return Math.toRadians(angleDegree);
	}

	public double getAccelx() {
		return accelx;
	}

	public void setAccelx(double accelx) {
		this.accelx = accelx;
	}

	public double getAccely() {
		return accely;
	}

	public void setAccely(double accely) {
		this.accely = accely;
	}

	/**
	 * Project velocity vector to X and Y axis.
	 */
	protected void calculateVectorProjection() {
		if(speed == 0) {
			this.speedx = 0;
			this.speedy = 0;
			return;
		}
		if (angleDegree == 0 || angleDegree == 360) {
			this.speedx = Math.abs(this.speed);
			this.speedy = 0;
		}
		else if (angleDegree == 90) {
			this.speedx = 0;
			this.speedy = Math.abs(this.speed);
		}
		else if (angleDegree == 180) {
			this.speedx = -Math.abs(this.speed);
			this.speedy = 0;
		}
		else if (angleDegree == 270) {
			this.speedx = 0;
			this.speedy = -Math.abs(this.speed);
		}
		else {
			double a = (this.angleDegree / PI_DEGRESS) * Math.PI;
			this.speedx = Math.cos(a) * this.speed;
			this.speedy = Math.sin(a) * this.speed;
		}
	}

	/**
	 * Join X and Y to velocity vector and calculate angle.
	 */
	protected void calculateJoinVector() {
		if(speedx == 0 && speedy == 0) {
			this.speed = 0;
		}
		if (speedx == 0) {
			this.speed = Math.abs(speedy);
			this.angleDegree = speedy >= 0 ? 90 : 270;
		}
		else if (speedy == 0) {
			this.speed = Math.abs(speedx);
			this.angleDegree = speedx >= 0 ? 0 : 180;
		}
		else {
			// Math.hypot() can be used if problems here.
			this.speed = Math.sqrt(speedx * speedx + speedy * speedy);
			if (speedy > 0) {
				this.angleDegree = ((Math.acos(speedx / speed) / Math.PI) * PI_DEGRESS);
			}
			else {
				this.angleDegree = ((Math.acos(-speedx / speed) / Math.PI) * PI_DEGRESS) + PI_DEGRESS;
			}
		}
	}

	@Override
	public String toString() {
		return "Speed=" + this.speed + ", Angle=" + this.angleDegree;
	}


	/**
	 * Whether same direction on X axis or Y axis.
	 * @param speed1
	 * @param speed2
	 * @return
	 */
	public static boolean isSameDirectionOnAxis(double speed1, double speed2) {
		return (speed1 * speed2) > 0.0;
	}
}
